`include "common_def.v"
//src1_key == 1 mean choose src1_forward as src1 for the EXU
`define LOAD_BIT 3
`define DST_BIT 2
`define SRC1_BIT 1
`define SRC2_BIT 0

`define IDR_BIT 0
`define EXR_BIT 1
`define LSR_BIT 2
`define REG_BIT 3
module MODULE_Forward_Unit (
	input														clk_i,
	input														rst_i,
	input														add_start_i,
// information used to save inst state
	input														IDR_wen_i,
	input														EXR_wen_i,
	input														LSR_wen_i,
	input	 	[3:0]										inst_key_IDR_i,
	input		[`R_ADDR_W-1:0]					addr_src1_IDR_i,
	input		[`R_ADDR_W-1:0]					addr_src2_IDR_i,
	input		[`R_ADDR_W-1:0]					addr_dst_IDR_i,

//data use to forward
	input		[`WIDTH-1:0]						data_dst_EXR_i,
	input		[`WIDTH-1:0]						data_dst_LSR_i,
	input		[`WIDTH-1:0]						load_data_LSR_i,	
	
	output													add_nouse_inst_o,
	output													src1_key_o,
	output													src2_key_o,
	output [`WIDTH-1:0]							src1_forward_o,
	output [`WIDTH-1:0]							src2_forward_o,
//conncect to Regs
	input		[`WIDTH-1:0]						data1_regs_i,
	output	[`R_ADDR_W-1:0]					addr1_regs_o,
	input		[`WIDTH-1:0]						data2_regs_i,
	output	[`R_ADDR_W-1:0]					addr2_regs_o
);
//regs to save the inst state
//wire	[2:0]	inst_now_state;
//wire	[2:0]	inst_last_state;
//wire	[2:0]	inst_last1_state;
wire	inst_in_IDR;
wire	inst_in_EXR;
wire 	inst_in_LSR;
Reg #(1,0) inst_in_IDR_reg (clk_i,rst_i,IDR_wen_i|add_start_i,inst_in_IDR,IDR_wen_i|add_start_i);
Reg #(1,0) inst_in_EXR_reg (clk_i,rst_i,IDR_wen_i|add_start_i? 1'b0:((inst_in_IDR&inst_in_EXR_last&inst_in_EXR_last1)|(~inst_in_IDR_last)|(~inst_in_IDR_last1))? 1'b1:inst_in_EXR,inst_in_EXR,IDR_wen_i|add_start_i|EXR_wen_i);
Reg #(1,0) inst_in_LSR_reg (clk_i,rst_i,IDR_wen_i|add_start_i? 1'b0:((inst_in_EXR&inst_in_LSR_last&inst_in_LSR_last1)|(~inst_in_IDR_last)|(~inst_in_IDR_last1))? 1'b1:inst_in_LSR,inst_in_LSR,IDR_wen_i|add_start_i|LSR_wen_i);
//assign inst_now_state = {inst_in_LSR,inst_in_EXR,inst_in_IDR};
wire	inst_in_IDR_last;
wire	inst_in_EXR_last;
wire	inst_in_LSR_last;
Reg #(1,0) inst_in_IDR_last_reg (clk_i,rst_i,inst_in_IDR,inst_in_IDR_last,IDR_wen_i|add_start_i);
Reg #(1,0) inst_in_EXR_last_reg (clk_i,rst_i,IDR_wen_i|add_start_i? inst_in_EXR|((~inst_in_EXR) &EXR_wen_i):(inst_in_IDR_last&inst_in_EXR_last1)? 1'b1:inst_in_EXR_last,inst_in_EXR_last,EXR_wen_i|IDR_wen_i|add_start_i);
Reg #(1,0) inst_in_LSR_last_reg (clk_i,rst_i,IDR_wen_i|add_start_i? inst_in_LSR|(inst_in_EXR&(~inst_in_LSR) &LSR_wen_i):(inst_in_EXR_last&inst_in_LSR_last1)? 1'b1:inst_in_LSR_last,inst_in_LSR_last,LSR_wen_i|IDR_wen_i|add_start_i);
//assign inst_last_state = {inst_in_LSR_last,inst_in_EXR_last,inst_in_IDR_last};
wire	inst_in_IDR_last1;
wire	inst_in_EXR_last1;
wire	inst_in_LSR_last1;
Reg #(1,0) inst_in_IDR_last1_reg (clk_i,rst_i,inst_in_IDR_last,inst_in_IDR_last1,IDR_wen_i|add_start_i);
Reg #(1,0) inst_in_EXR_last1_reg (clk_i,rst_i,IDR_wen_i|add_start_i?inst_in_EXR_last|((~inst_in_EXR_last)&EXR_wen_i):(inst_in_IDR_last1)? 1'b1:inst_in_EXR_last1,inst_in_EXR_last1,EXR_wen_i|IDR_wen_i|add_start_i);
Reg #(1,0) inst_in_LSR_last1_reg (clk_i,rst_i,IDR_wen_i|add_start_i?inst_in_LSR_last|(inst_in_EXR_last&(~inst_in_LSR_last)&LSR_wen_i):(inst_in_EXR_last1)? 1'b1:inst_in_LSR_last1,inst_in_LSR_last1,LSR_wen_i|IDR_wen_i|add_start_i);
//assign inst_last1_state = {inst_in_LSR_last1,inst_in_EXR_last1,inst_in_IDR_last1};

//regs to save the inst information
wire	 	[3:0]										inst_key_now;
wire		[`R_ADDR_W-1:0]					addr_src1_now;
wire		[`R_ADDR_W-1:0]					addr_src2_now;
wire		[`R_ADDR_W-1:0]					addr_dst_now;
wire	 	[3:0]										inst_key_last;
//wire		[`R_ADDR_W-1:0]					addr_src1_last;
//wire		[`R_ADDR_W-1:0]					addr_src2_last;
wire		[`R_ADDR_W-1:0]					addr_dst_last;
wire	 	[3:0]										inst_key_last1;
//wire		[`R_ADDR_W-1:0]					addr_src1_last1;
//wire		[`R_ADDR_W-1:0]					addr_src2_last1;
wire		[`R_ADDR_W-1:0]					addr_dst_last1;
wire [`R_ADDR_W-1:0]						addr_dst_last2;
wire	[3:0]											inst_key_last2;

assign inst_key_now = inst_key_IDR_i;
assign addr_src1_now = addr_src1_IDR_i;
assign addr_src2_now = addr_src2_IDR_i;
assign addr_dst_now = addr_dst_IDR_i;
Reg #(4,0) reg_inst_key_last(clk_i,rst_i,add_start_i ? 0:inst_key_now,inst_key_last,IDR_wen_i|add_start_i);
//Reg #(`R_ADDR_W,0) reg_addr_src1_last(clk_i,rst_i,addr_src1_now,addr_src1_last,IDR_wen_i|add_start_i);
//Reg #(`R_ADDR_W,0) reg_addr_src2_last(clk_i,rst_i,addr_src2_now,addr_src2_last,IDR_wen_i|add_start_i);
Reg #(`R_ADDR_W,0) reg_addr_dst_last(clk_i,rst_i,addr_dst_now,addr_dst_last,IDR_wen_i|add_start_i);
Reg #(4,0) reg_inst_key_last1(clk_i,rst_i,inst_key_last,inst_key_last1,IDR_wen_i|add_start_i);
//Reg #(`R_ADDR_W,0) reg_addr_src1_last1(clk_i,rst_i,addr_src1_last,addr_src1_last1,IDR_wen_i|add_start_i);
//Reg #(`R_ADDR_W,0) reg_addr_src2_last1(clk_i,rst_i,addr_src2_last,addr_src2_last1,IDR_wen_i|add_start_i);
Reg #(`R_ADDR_W,0) reg_addr_dst_last1(clk_i,rst_i,addr_dst_last,addr_dst_last1,IDR_wen_i|add_start_i);
Reg #(`R_ADDR_W,0)	addr_dst_last2_reg(clk_i,rst_i,addr_dst_last1,addr_dst_last2,IDR_wen_i|add_start_i);
Reg #(4,0)	inst_key_last2_reg(clk_i,rst_i,inst_key_last1,inst_key_last2,IDR_wen_i|add_start_i);

//judge if hazard
wire src1_hazard_with_last_dst;
wire src1_hazard_with_last1_dst;
wire src2_hazard_with_last_dst;
wire src2_hazard_with_last1_dst;
assign src1_hazard_with_last_dst = inst_key_now[`SRC1_BIT] & inst_key_last[`DST_BIT] & (addr_src1_now == addr_dst_last) &(addr_src1_now != 0);
assign src1_hazard_with_last1_dst = inst_key_now[`SRC1_BIT] & inst_key_last1[`DST_BIT] & (addr_src1_now == addr_dst_last1)&(addr_src1_now != 0);
assign src2_hazard_with_last_dst = inst_key_now[`SRC2_BIT] & inst_key_last[`DST_BIT] & (addr_src2_now == addr_dst_last)&(addr_src2_now != 0);
assign src2_hazard_with_last1_dst = inst_key_now[`SRC2_BIT] & inst_key_last1[`DST_BIT] & (addr_src2_now == addr_dst_last1)&(addr_src2_now != 0);


wire [1:0] key_src1_forward;
wire [1:0] key_src2_forward;
assign key_src1_forward = {src1_hazard_with_last1_dst,src1_hazard_with_last_dst}; 
assign key_src2_forward = {src2_hazard_with_last1_dst,src2_hazard_with_last_dst}; 

//save data1_regs_i;when  addr_dst_now == addr_src_now  in jump inst, when
//waiting wash,the regs may have be changed,if we don't save the data,we can't
//forward right data
wire [`WIDTH-1:0] data1_regs_save;
wire [`WIDTH-1:0] data2_regs_save;
wire [`WIDTH-1:0] data1_regs_save0;
wire [`WIDTH-1:0] data2_regs_save0;
wire	inst_now_state_lsr_rise;
wire	inst_now_state_lsr_rise_r;
Reg #(1,0) inst_now_state_lsr_rise_reg(clk_i,rst_i,inst_in_LSR,inst_now_state_lsr_rise_r,1);
assign inst_now_state_lsr_rise = inst_in_LSR & (~inst_now_state_lsr_rise_r);
Reg #(`WIDTH,0) data1_regs_save_reg(clk_i,rst_i,data1_regs_i,data1_regs_save0,inst_now_state_lsr_rise);
assign data1_regs_save = inst_now_state_lsr_rise ? data1_regs_i : data1_regs_save0;
Reg #(`WIDTH,0) data2_regs_save_reg(clk_i,rst_i,data2_regs_i,data2_regs_save0,inst_now_state_lsr_rise);
assign data2_regs_save = inst_now_state_lsr_rise ? data2_regs_i : data2_regs_save0;


wire [`WIDTH-1:0]	src1_forward;
wire [`WIDTH-1:0]	src2_forward;
MuxKeyWithDefault #(4,2,`WIDTH) src1_forward_mux(.out(src1_forward[`WIDTH-1:0]),.key(key_src1_forward[1:0]),.default_out(0),.lut({
	2'b00	,	64'b0,
	2'b01	,	inst_in_LSR?(addr_dst_now == addr_src1_now?data1_regs_save:data1_regs_i):inst_in_LSR_last? (inst_key_last[`LOAD_BIT]? load_data_LSR_i:data_dst_LSR_i):data_dst_EXR_i,
	2'b10 , inst_in_LSR_last? data1_regs_i :inst_key_last1[`LOAD_BIT]? load_data_LSR_i:data_dst_LSR_i,
	2'b11	,	inst_in_LSR?(addr_dst_now == addr_src1_now?data1_regs_save:data1_regs_i):inst_in_LSR_last? (inst_key_last[`LOAD_BIT]? load_data_LSR_i:data_dst_LSR_i):data_dst_EXR_i
}));
MuxKeyWithDefault #(4,2,`WIDTH) src2_forward_mux(.out(src2_forward[`WIDTH-1:0]),.key(key_src2_forward[1:0]),.default_out(0),.lut({
	2'b00	,	64'b0,
	2'b01	,inst_in_LSR ?(addr_dst_now == addr_src2_now?data2_regs_save:data2_regs_i):inst_in_LSR_last? (inst_key_last[`LOAD_BIT]? load_data_LSR_i:data_dst_LSR_i):data_dst_EXR_i,
	2'b10 , inst_in_LSR_last? data2_regs_i :inst_key_last1[`LOAD_BIT]? load_data_LSR_i:data_dst_LSR_i,
	2'b11	,	inst_in_LSR ?(addr_dst_now == addr_src2_now?data2_regs_save:data2_regs_i):inst_in_LSR_last? (inst_key_last[`LOAD_BIT]? load_data_LSR_i:data_dst_LSR_i):data_dst_EXR_i
}));
assign add_nouse_inst_o	=(src1_hazard_with_last_dst|src2_hazard_with_last_dst) & inst_key_last[`LOAD_BIT] & (~inst_in_LSR_last);


wire src1_hazzard_with_last2_dst;
wire src2_hazzard_with_last2_dst;
assign src1_hazzard_with_last2_dst = inst_key_now[`SRC1_BIT] & inst_key_last2[`DST_BIT] & (addr_src1_now == addr_dst_last2) & inst_key_last2[`LOAD_BIT];
assign src2_hazzard_with_last2_dst = inst_key_now[`SRC2_BIT] & inst_key_last2[`DST_BIT] & (addr_src2_now == addr_dst_last2) & inst_key_last2[`LOAD_BIT];
assign src1_key_o = src1_hazard_with_last_dst|src1_hazard_with_last1_dst|src1_hazzard_with_last2_dst ;
assign src2_key_o = src2_hazard_with_last_dst|src2_hazard_with_last1_dst|src2_hazzard_with_last2_dst ;
assign src1_forward_o = (~src1_hazard_with_last1_dst)&(~src1_hazard_with_last_dst) & src1_hazzard_with_last2_dst ? data1_regs_i : src1_forward;
assign src2_forward_o = (~src2_hazard_with_last1_dst)&(~src2_hazard_with_last_dst) & src2_hazzard_with_last2_dst ? data2_regs_i : src2_forward;

assign addr1_regs_o [`R_ADDR_W-1:0] = (src1_hazzard_with_last2_dst) ? addr_dst_last2:
																		(src1_hazard_with_last_dst) ? addr_dst_last:addr_dst_last1;
assign addr2_regs_o [`R_ADDR_W-1:0] = (src2_hazzard_with_last2_dst) ? addr_dst_last2:
																		(src2_hazard_with_last_dst) ? addr_dst_last:addr_dst_last1;

endmodule
